﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Runtime.Serialization;
using EntityFrameworkContrib.PostSharp4EF;
using System.Data.Objects;
using System.Collections;
using CyclicReferences.Public;

namespace EFContrib.PS4EF.Tst.InheritanceTests
{
    /// <summary>
    /// Summary description for InheritanceTest
    /// </summary>
    [TestClass]
    public class InheritanceTest
    {
        #region setup
        public InheritanceTest()
        {
            //
            // TODO: Add constructor logic here
            //
        }

        private TestContext testContextInstance;

        /// <summary>
        ///Gets or sets the test context which provides
        ///information about and functionality for the current test run.
        ///</summary>
        public TestContext TestContext
        {
            get
            {
                return testContextInstance;
            }
            set
            {
                testContextInstance = value;
            }
        }

        #region Additional test attributes
        //
        // You can use the following additional attributes as you write your tests:
        //
        // Use ClassInitialize to run code before running the first test in the class
        // [ClassInitialize()]
        // public static void MyClassInitialize(TestContext testContext) { }
        //
        // Use ClassCleanup to run code after all tests in a class have run
        // [ClassCleanup()]
        // public static void MyClassCleanup() { }
        //
        // Use TestInitialize to run code before running each test 
        // [TestInitialize()]
        // public void MyTestInitialize() { }
        //
        // Use TestCleanup to run code after each test has run
        // [TestCleanup()]
        // public void MyTestCleanup() { }
        //
        #endregion 
        #endregion

        [TestMethod]
        public void tstBasicInheritance()
        {
            string MsgOnWire = String.Empty;
            int id;
            using (InheritanceTypeConnection context = new InheritanceTypeConnection())
            {
                Customer c = new Customer { Firstname = "Ruurd", Lastname = "Boeke", CustomerDiscount = 5 };
                context.AddToPerson(c);

                context.SaveChanges();
                id = c.PersonID;

                MsgOnWire = context.Serialize(c);

            }

            // deserialize
            CustomerClient cClient = MsgOnWire.DeserializeForClient<CustomerClient>();
            cClient.Firstname = "changedAt" + DateTime.Now.ToString();
            string nameToCheck = cClient.Firstname;
            cClient.CustomerDiscount = 10;

            // serialize
            MsgOnWire = cClient.SerializeForClient();


            using (InheritanceTypeConnection context = new InheritanceTypeConnection())
            {
                Customer c = context.Deserialize<Customer>(MsgOnWire, obj => "Person");

                context.SaveChanges();
            }


            using (InheritanceTypeConnection context = new InheritanceTypeConnection())
            {
                Customer c = (Customer) (from p in context.Person
                              where p.PersonID == id
                              select p).First();

                Assert.IsTrue(c.Firstname == nameToCheck);
                Assert.IsTrue(c.CustomerDiscount == 10);
            }
            
        }

        [TestMethod]
        public void tstBigGraphInheritance()
        {
            using (InheritanceTypeConnection context = new InheritanceTypeConnection())
            {
                var persons = from p in context.Person
                              select p;
                foreach (Person p in persons)
                    context.DeleteObject(p);

                context.SaveChanges();
            }

            string MsgOnWire = String.Empty;
            int id;
            using (InheritanceTypeConnection context = new InheritanceTypeConnection())
            {

                Einstein lead = new Einstein { Firstname = "Bill", Lastname = "G" };
                context.AddToPerson(lead);

                Mort e1 = new Mort { Firstname = "Ruurd", Lastname = "Boeke", Language = "C#", EF=true, WCF=true, WF= true, WPF=true, TeamLeader=lead };
                context.AddToPerson(e1);

                Elvis e2 = new Elvis { Firstname = "Elvis", Lastname = "Presley", Language = "Java", EF = false, WCF = true, WF = false, WPF = true, TeamLeader = lead };
                context.AddToPerson(e2);

                context.SaveChanges();
                id = e1.PersonID;

                MsgOnWire = context.Serialize(e1);  // we'll take mort as the graphroot (no importance)
            }

            // deserialize
            MortClient mClient = MsgOnWire.DeserializeForClient<MortClient>();
            mClient.Firstname = "changedAt" + DateTime.Now.ToString();
            string nameToCheck = mClient.Firstname;
            mClient.EF = false; // mort tends to forget skills

            EmployeeClient teamlead = mClient.TeamLeader;

            // elvis is a free soul, he can not be managed.
            ElvisClient eClient = (ElvisClient) mClient.TeamLeader.TeamMembers.Last();
            mClient.TeamLeader.TeamMembers.Remove(eClient);
            eClient.TeamLeader = null;

            // always add more einsteins in the mix
            EinsteinClient einClient = new EinsteinClient { EF = true, WCF = true, WF = false, WPF = false, TeamLeader = teamlead, Firstname = "Albert", Language = "C#", Lastname = "Einstein" };
            teamlead.TeamMembers.Add(einClient);


            // serialize
            MsgOnWire = mClient.SerializeForClient();


            using (InheritanceTypeConnection context = new InheritanceTypeConnection())
            {

                // deserialize
                Mort m = context.Deserialize<Mort>(MsgOnWire);

                context.SaveChanges();
            }

        }
    }

    #region extensions for serializing
    public static class SerializeHelp
    {
        public static string Serialize(this ObjectContext context, object graphroot)
        {
            ((IContextAware) graphroot).CreateSerializableState(context);

            //List<Type> knownTypes = (from type in graphroot.GetType().Assembly.GetTypes()
            //                         where type.Name.Contains("Surrogate")
            //                         select type).ToList();

            List<Type> knownTypes = new List<Type>();
            foreach(Type t in graphroot.GetType().Assembly.GetTypes())
                if (t.Name.Contains("Surrogate") && t.Name.Contains("Client") == false && t.FullName.Contains("Inheritance")) 
                { knownTypes.Add(t); }

            knownTypes.Add(new List<Employee>().GetType());

            DataContractSerializer s = new DataContractSerializer(graphroot.GetType(), knownTypes, int.MaxValue, false, false, new SubstituteDomainDataContractSurrogate());
            return s.GetWellFormedToContract(graphroot);
        }

        public static string Serialize(this ObjectContext context, IList listOfObjects)
        {
            foreach (object item in listOfObjects)
                ((IContextAware) item).CreateSerializableState(context);

            DataContractSerializer s = new DataContractSerializer(listOfObjects.GetType(), null, int.MaxValue, false, false, new SubstituteDomainDataContractSurrogate());
            return s.GetWellFormedToContract(listOfObjects);
        }

        public static T Deserialize<T>(this ObjectContext context, string msg)
        {
            List<Type> knownTypes = new List<Type>();
            foreach (Type t in typeof(T).Assembly.GetTypes())
                if (t.Name.Contains("Surrogate") && t.Name.Contains("Client") == false && t.FullName.Contains("Inheritance"))
                { knownTypes.Add(t); }

            knownTypes.Add(new List<Employee>().GetType());


            DataContractSerializer s = new DataContractSerializer(typeof(T), knownTypes, int.MaxValue, false, false, new SubstituteDomainDataContractSurrogate());
            T p = (T) s.UnwrapFromString(msg);

            ((IContextAware) p).AttachGraphToContext(context, source => "Person");

            return p;

        }

        public static T Deserialize<T>(this ObjectContext context, string msg, Func<object, string> GetEntitySet)
        {
            List<Type> knownTypes = (from type in typeof(T).Assembly.GetTypes()
                                     where type.Name.IndexOf("Surrogate") > 0
                                     select type).ToList();


            DataContractSerializer s = new DataContractSerializer(typeof(T), knownTypes, int.MaxValue, false, false, new SubstituteDomainDataContractSurrogate());
            T p = (T) s.UnwrapFromString(msg);

            ((IContextAware) p).AttachGraphToContext(context, GetEntitySet);

            return p;

        }

        public static T DeserializeForClient<T>(this string msg)
        {
            List<Type> knownTypes = new List<Type>();
            foreach (Type t in typeof(T).Assembly.GetTypes())
                if (t.Name.Contains("Surrogate") && t.Name.Contains("Client") && t.FullName.Contains("Inheritance"))
                { knownTypes.Add(t); }

            knownTypes.Add(new List<EmployeeClient>().GetType());

            DataContractSerializer s = new DataContractSerializer(typeof(T), knownTypes, int.MaxValue, false, false, new SubstituteDomainDataContractSurrogate());

            return (T) s.UnwrapFromString(msg);
        }

        public static string SerializeForClient(this object p)
        {
            List<Type> knownTypes = new List<Type>();
            foreach (Type t in p.GetType().Assembly.GetTypes())
                if (t.Name.Contains("Surrogate") && t.Name.Contains("Client") && t.FullName.Contains("Inheritance"))
                { knownTypes.Add(t); }

            knownTypes.Add(new List<EmployeeClient>().GetType());

            DataContractSerializer s = new DataContractSerializer(p.GetType(), knownTypes, int.MaxValue, false, false, new SubstituteDomainDataContractSurrogate());

            return s.GetWellFormedToContract(p);
        }
    }

    #endregion
}
